/*******************************************************************************

                      .AVI文件解析器实现

本模块 从SWM341官方 AVI_PARSER_parser模块起整理，同时与SWM硬件解耦以通用化
参考：https://blog.csdn.net/weixin_47852035/article/details/121577751
********************************************************************************/
#include "AviParser.h"
#include <string.h>

//----------------------------------内部宏函数---------------------------------
#define _MAKEDWORD(ptr) (uint32_t)(((uint16_t) * (uint8_t *)(ptr) | \
                        (((uint16_t) * (uint8_t *)(ptr + 1)) << 8) | \
                        (((uint16_t) * (uint8_t *)(ptr + 2)) << 16) | \
                        (((uint16_t) * (uint8_t *)(ptr + 3)) << 24)))

//---------------------------Avi文件解析---------------------------------
//返回AVI_PARSER_OK文件解析成功(此时AviStream位于"movi"后), 其它错误代码
eAviParserStatus_t AviParser(AviParserInfo_t *pAviParserInfo,
                             GetAviStream_t GetAviStream, void *pObj)
{
  //读取文件头及首个LIST(固定为'hdrl')头信息
  int size = sizeof(AVI_PARSER_HEADER) + sizeof(AVI_PARSER_LIST_HEADER);
  int cursize = size;
  const uint8_t *buf = GetAviStream(pObj, &cursize);
  if((buf == NULL) || (cursize < size)) return AVI_PARSER_LIST_ERR;
  //header检测
  AVI_PARSER_HEADER *AVI_PARSERheader = (AVI_PARSER_HEADER *)buf;
  if (AVI_PARSERheader->RiffID != AVI_PARSER_RIFF_ID) 
    return AVI_PARSER_RIFF_ERR; //RIFF ID错误
  if (AVI_PARSERheader->AVI_PARSERID != AVI_PARSER_AVI_ID) 
    return AVI_PARSER_AVI_PARSER_ERR; //AVI ID错误
  //LIST 'hdrl'检测
  buf += sizeof(AVI_PARSER_HEADER);
  AVI_PARSER_LIST_HEADER *listheader = (AVI_PARSER_LIST_HEADER *)(buf);
  if(listheader->ListID != AVI_PARSER_LIST_ID) 
    return AVI_PARSER_LIST_ERR; //LIST ID错误
  if (listheader->ListType != AVI_PARSER_HDRL_ID)  
    return AVI_PARSER_HDRL_ERR; //HDRL ID错误

  //获取首个一级LIST块('hdrl')全部内容(内含多个下级LIST)
  int BlockSize = listheader->BlockSize;
  size = BlockSize + sizeof(AVI_PARSER_LIST_HEADER) - 4;//多读了4个
  cursize = size;
  buf = GetAviStream(pObj, &cursize);
  if((buf == NULL) || (cursize < size)) return AVI_PARSER_LIST_ERR;
  const unsigned char *nextlist = (buf + BlockSize - 4);
  //avih域解析
  AVI_PARSER_HHEADER *AVI_PARSERhheader = (AVI_PARSER_HHEADER *)(buf);
  if (AVI_PARSERhheader->BlockID != AVI_PARSER_AVIH_ID)  
    return AVI_PARSER_AVI_PARSERH_ERR; //AVIH ID错误
  pAviParserInfo->SecPerFrame = AVI_PARSERhheader->SecPerFrame; //得到帧间隔时间
  pAviParserInfo->TotalFrame = AVI_PARSERhheader->TotalFrame;   //得到总帧数
  pAviParserInfo->Width = AVI_PARSERhheader->Width;
  pAviParserInfo->Height = AVI_PARSERhheader->Height;
  //首个二级LIST块: LIST 'strl'
  buf += AVI_PARSERhheader->BlockSize + 8;           //偏移
  listheader = (AVI_PARSER_LIST_HEADER *)(buf);
  if (listheader->ListID != AVI_PARSER_LIST_ID)  
    return AVI_PARSER_LIST_ERR; //LIST ID错误
  if (listheader->ListType != AVI_PARSER_STRL_ID) 
    return AVI_PARSER_STRL_ERR; //STRL ID错误
  //LIST 'strl'->strh
  AVI_PARSER_STRH_HEADER *strhheader = (AVI_PARSER_STRH_HEADER *)(buf + 12);
  if (strhheader->BlockID != AVI_PARSER_STRH_ID)  
    return AVI_PARSER_STRH_ERR; //STRH ID错误

  if (strhheader->StreamType == AVI_PARSER_VIDS_STREAM){ //视频帧在前
    pAviParserInfo->VideoType = strhheader->Handler;
    AVI_PARSER_STRF_BMPHEADER *bmpheader = (AVI_PARSER_STRF_BMPHEADER *)
                               (buf + 12 + strhheader->BlockSize + 8); //strf
    if (bmpheader->BlockID != AVI_PARSER_STRF_ID)
        return AVI_PARSER_STRF_ERR; //STRF ID错误

    //pAviParserInfo->Width = bmpheader->bmiHeader.Width;
    //pAviParserInfo->Height = bmpheader->bmiHeader.Height;
    buf += listheader->BlockSize + 8; //偏移
    listheader = (AVI_PARSER_LIST_HEADER *)(buf);
    if (listheader->ListID != AVI_PARSER_LIST_ID){ //是不含有音频帧的视频文件
      pAviParserInfo->SampleRate = 0; //音频采样率
      pAviParserInfo->Channels = 0;   //音频通道数
      pAviParserInfo->AudioType = 0;  //音频格式
    }
    else{
      if (listheader->ListType != AVI_PARSER_STRL_ID) 
        return AVI_PARSER_STRL_ERR; //STRL ID错误
      strhheader = (AVI_PARSER_STRH_HEADER *)(buf + 12);
      if (strhheader->BlockID != AVI_PARSER_STRH_ID)
          return AVI_PARSER_STRH_ERR; //STRH ID错误
      if (strhheader->StreamType != AVI_PARSER_AUDS_STREAM)
          return AVI_PARSER_FORMAT_ERR; //格式错误
      AVI_PARSER_STRF_WAVHEADER *wavheader = (AVI_PARSER_STRF_WAVHEADER *)
                                        (buf + 12 + strhheader->BlockSize + 8);
      if (wavheader->BlockID != AVI_PARSER_STRF_ID)  
        return AVI_PARSER_STRF_ERR; //STRF ID错误

      pAviParserInfo->SampleRate = wavheader->SampleRate; //音频采样率
      pAviParserInfo->Channels = wavheader->Channels;     //音频通道数
      pAviParserInfo->AudioType = wavheader->FormatTag;   //音频格式
    }
  }
  else if (strhheader->StreamType == AVI_PARSER_AUDS_STREAM){ //音频帧在前
    pAviParserInfo->VideoType = strhheader->Handler;
    AVI_PARSER_STRF_WAVHEADER *wavheader = (AVI_PARSER_STRF_WAVHEADER *)
                                (buf + 12 + strhheader->BlockSize + 8); //strf
    if (wavheader->BlockID != AVI_PARSER_STRF_ID) 
      return AVI_PARSER_STRF_ERR; //STRF ID错误

    pAviParserInfo->SampleRate = wavheader->SampleRate; //音频采样率
    pAviParserInfo->Channels = wavheader->Channels;     //音频通道数
    pAviParserInfo->AudioType = wavheader->FormatTag;   //音频格式
    buf += listheader->BlockSize + 8;        //偏移
    listheader = (AVI_PARSER_LIST_HEADER *)(buf);
    if (listheader->ListID != AVI_PARSER_LIST_ID) 
      return AVI_PARSER_LIST_ERR; //LIST ID错误

    if (listheader->ListType != AVI_PARSER_STRL_ID) 
      return AVI_PARSER_STRL_ERR; //STRL ID错误

    strhheader = (AVI_PARSER_STRH_HEADER *)(buf + 12);
    if (strhheader->BlockID != AVI_PARSER_STRH_ID) 
      return AVI_PARSER_STRH_ERR; //STRH ID错误

    if (strhheader->StreamType != AVI_PARSER_VIDS_STREAM) 
      return AVI_PARSER_FORMAT_ERR; //格式错误

    AVI_PARSER_STRF_BMPHEADER *bmpheader = (AVI_PARSER_STRF_BMPHEADER *)
                                (buf + 12 + strhheader->BlockSize + 8); //strf
    if (bmpheader->BlockID != AVI_PARSER_STRF_ID)  
      return AVI_PARSER_STRF_ERR; //STRF ID错误

    //pAviParserInfo->Width = bmpheader->bmiHeader.Width;
    //pAviParserInfo->Height = bmpheader->bmiHeader.Height;
  }

  //将文移至LISFT "movi" 末尾，即音视频流的起始
  //LIST有未对齐情况,故使用copy
  AVI_PARSER_LIST_HEADER ListHeader;
  do{
    memcpy(&ListHeader, nextlist, sizeof(AVI_PARSER_LIST_HEADER));
    if((ListHeader.ListID != AVI_PARSER_LIST_ID) && 
       (ListHeader.ListID != AVI_PARSER_JUNK_ID)) //JUNK段
      return AVI_PARSER_LIST_ERR; //LIST ID错误
    if(ListHeader.ListType == AVI_PARSER_MOVI_ID){//找到了
      //这里检查略
      break;
    }
    //没找到，继续下一个一级LIST或JUNK
    size = ListHeader.BlockSize + sizeof(AVI_PARSER_LIST_HEADER)- 4;//多读了4个
    cursize = size;
    nextlist = GetAviStream(pObj, &cursize);
    if((nextlist == NULL) || (cursize < size)) return AVI_PARSER_LIST_ERR;
    nextlist += size - sizeof(AVI_PARSER_LIST_HEADER);
  }while(1);
  
  return AVI_PARSER_OK;
}

//------------------------由音视频流数据头获取流信息----------------------------
//返回值为位定义: b0：0或1标志, b1:音频0视频1标志  b2:0未压缩/1压缩标志
//                b3: 0音视频帧标志,否则为pc帧, b8:未识别到标志
//即返回值 > 7时为非音视频标志.
//如01dc,01wb,第2字0或1为交替出现(不区分音频或视频), pc：改用新的调色板
//db:未压缩的视频帧, dc:压缩的视频帧, wb:未压缩音频数据, wc压缩的音频数据
uint8_t AviParser_StreamHeader2Info(const AVI_PARSER_STREAM_HEADER *pSH)
{
  if(pSH->Header[0] != '0') return 0xff; //头错误
  uint8_t No;
  if(pSH->Header[1] == '0') No = 0x00;
  else if(pSH->Header[1] == '1') No = 0x01;
  else return 0xff; //头错误
  
  if(pSH->Header[2] == 'd'){//视频帧
    if(pSH->Header[3] == 'c') return No | 0x02 | 0x04; //dc:压缩的视频帧
    if(pSH->Header[3] == 'b') return No | 0x02 | 0x00; //db:未压缩的视频帧
  }
  else if(pSH->Header[2] == 'w'){//音频帧
    if(pSH->Header[3] == 'b') return No | 0x00 | 0x00;//wb:未压缩音频数据
    if(pSH->Header[3] == 'c') return No | 0x00 | 0x04;//wb:压缩音频数据
  }
  else if(pSH->Header[2] == 'p'){
    if(pSH->Header[3] == 'c') return No | 0x08;// pc：改用新的调色板
  }
  return 0xff;  
}

//--------------------------------判断有效头---------------------------------
uint8_t AviParser_IsStreamHeaderL(uint8_t H, uint8_t L)//低2位匹配检查
{
  if(H == 'd'){//视频帧
    if(L == 'c') return 1; //dc:压缩的视频帧
    if(L == 'b') return 1; //db:未压缩的视频帧
  }
  else if(H == 'w'){//音频帧
    if(L == 'b') return 1;//wb:未压缩音频数据
    if(L == 'c') return 1;//wb:压缩音频数据
  }
  else if(H == 'p'){
    if(L == 'c') return 1;// pc：改用新的调色板
  }
  return 0;  
}

//---------------------------由音视频流数据头获取块大小-------------------------
//AviParser_StreamHeader2Type()非0时有效，值含头自身大小
uint32_t AviParser_StreamHeader2Size(const AVI_PARSER_STREAM_HEADER *pSH)
{
  return _MAKEDWORD(pSH->BlockSize);
}




